package cs423.loadBalancer.core;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import cs423.loadBalancer.core.Adaptor.waitOnState;
import cs423.loadBalancer.core.HardwareMonitor.gatherInfo;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.BatteryManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.format.Formatter;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
import android.widget.Toast;

public class Main extends Activity {
	public static final int AGRREGATIONPORT = 8060;
	public static final int BOOTSRAPPORT = 8070;
	public static final int STATEPORT = 8080;
	public static final int TRANSFERPORT = 8090;
	static byte[] orgInput;
	Socket ClientS;
	EditText ip;
	volatile static int multiplier;
	static int width;
	SeekBar throttleSeekBar;
	// int throttleValue;
	TextView throttlView;
	static String ipAddress;
	volatile static double[][] data;
	static int interval;
	static volatile double running_time = 0;
	static volatile double other_percentage;
	static volatile String func_kind;

	static final int CLOSE = 0;
	static final int ALIVE = 1;
	static final int CHUAN = 1;
	static final int BUCHUAN = 0;

	volatile static List<Integer> cut_pos;
	volatile static List<Integer> final_result;
	volatile static int initial_q_size;
	volatile static int num_threads = 1;

	// RIGHT NOW IT IS INTEGER, MAYBE CHANGED IN THE FUTURE
	volatile static float cpu_usage;
	volatile static int throttle = 0;
	volatile static int batteryLevel = 0;
	volatile static int passive_transfer_decision;// 0==false, 1==true
	volatile static int active_transfer_decision;// 0==false, 1==true

	volatile static boolean other_side_closed = false;

	static Semaphore trans_w_adaptor = new Semaphore(0, true);

	// static Lock adaptor_trans_lock = new ReentrantLock();
	// static Condition trans_w_adaptor = adaptor_trans_lock.newCondition();

	static Semaphore monitor_w_adaptor = new Semaphore(0, true);
	// static Lock adaptor_monitor_lock = new ReentrantLock();
	// static Condition monitor_w_adaptor = adaptor_monitor_lock.newCondition();

	static Semaphore state_tri_adaptor_s_a_cv = new Semaphore(0, true);
	// static Lock state_tri_adaptor_s_a_lock = new ReentrantLock();
	// static Condition state_tri_adaptor_s_a_cv = state_tri_adaptor_s_a_lock
	// .newCondition();

	static Semaphore state_tri_adaptor_a_s_cv = new Semaphore(0, true);
	// static Lock state_tri_adaptor_a_s_lock = new ReentrantLock();
	// static Condition state_tri_adaptor_a_s_cv = state_tri_adaptor_a_s_lock
	// .newCondition();

	static Semaphore adaptor_worker_cv = new Semaphore(0, true);
	// static Lock adaptor_worker_lock = new ReentrantLock();
	// static Condition adaptor_worker_cv = adaptor_worker_lock.newCondition();

	static Semaphore adaptor_tri_state_a_s_cv = new Semaphore(0, true);
	// static Lock adaptor_tri_state_a_s_lock = new ReentrantLock();
	// static Condition adaptor_tri_state_a_s_cv = adaptor_tri_state_a_s_lock
	// .newCondition();

	static Semaphore adaptor_tri_state_s_a_cv = new Semaphore(0, true);
	// static Lock adaptor_tri_state_s_a_lock = new ReentrantLock();
	// static Condition adaptor_tri_state_s_a_cv = adaptor_tri_state_s_a_lock
	// .newCondition();

	static Semaphore worker_state_cv = new Semaphore(0, true);
	// static Lock worker_state = new ReentrantLock();
	// static Condition worker_state_cv = worker_state.newCondition();

	static Semaphore finish_cv = new Semaphore(0, true);
	// static Lock finish = new ReentrantLock();
	// static Condition finish_cv = finish.newCondition();

	static Semaphore tm_cv = new Semaphore(0, true);
	static Semaphore a_cv = new Semaphore(0, true);
	static Semaphore hm_cv = new Semaphore(0, true);
	static Semaphore sm_cv = new Semaphore(0, true);
	static Semaphore wt_cv = new Semaphore(0, true);
	/*
	 * static Lock tm = new ReentrantLock(); static Condition tm_cv =
	 * tm.newCondition(); static Lock a = new ReentrantLock(); static Condition
	 * a_cv = a.newCondition(); static Lock hm = new ReentrantLock(); static
	 * Condition hm_cv = hm.newCondition(); static Lock sm = new
	 * ReentrantLock(); static Condition sm_cv = sm.newCondition(); static Lock
	 * wt = new ReentrantLock(); static Condition wt_cv = wt.newCondition();
	 */

	static volatile int WORKER = 1;
	static volatile int STATE = 0;
	TextView contentTxt;
	static SeekBar threadSeekBar;
	static TextView jobsize;
	static TextView initiateTransfer;
	public static displayHandler displayHandler;
	TextView threadView;
	private TextView ipView;
	private Button connect;
	private Button wait;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		setContentView(R.layout.main);
		
		ip = (EditText) findViewById(R.id.entry);
		connect = (Button) findViewById(R.id.btnConnect);
		wait = (Button) findViewById(R.id.btnWait);
		throttleSeekBar = (SeekBar) findViewById(R.id.throbar);
		threadSeekBar = (SeekBar) findViewById(R.id.thread);
		threadSeekBar.setMax(9);
		throttlView = (TextView) findViewById(R.id.throttleValue);
		threadView = (TextView) findViewById(R.id.threadNum);
		jobsize = (TextView) findViewById(R.id.jobsize);
		initiateTransfer = (TextView) findViewById(R.id.initiateTransfer);
		displayHandler = new displayHandler();

		ipView = (TextView) findViewById(R.id.localIP);

		jobsize.setText("Remain jobs :");
		initiateTransfer.setText("Start tranfering :");
		
		setSeekBarListerner();
		
        setConnectAndWaitListener();		

		getBatteryInfo();
      
		displayIP();
		

	}

	private void setConnectAndWaitListener() {
		connect.setOnClickListener(new Button.OnClickListener() {
			public void onClick(View v) {
				ipAddress = ip.getText().toString();
				connect(ipAddress);
			}
		});
		wait.setOnClickListener(new Button.OnClickListener() {
			public void onClick(View v) {
				waitForConnection();
			}
		});
		
	}

	private void getBatteryInfo() {

		contentTxt = (TextView) findViewById(R.id.batteryLevel);

		BroadcastReceiver batteryReceiver = new MyBroadcastReceiver(contentTxt);

		IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);

		registerReceiver(batteryReceiver, filter);
		
	}

	private void displayIP() {
		WifiManager wifiManager = (WifiManager) getSystemService(WIFI_SERVICE);
		WifiInfo wifiInfo = wifiManager.getConnectionInfo();
		int myLocalIP = wifiInfo.getIpAddress();
		ipView.setText("My IP address is "
				+ Formatter.formatIpAddress(myLocalIP));
		
	}

	private void setSeekBarListerner() {
		throttleSeekBar
				.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {

					@Override
					public void onProgressChanged(SeekBar seekBar,
							int progress, boolean fromUser) {
						System.out.println("on progress progress is "
								+ progress);

						throttle = progress;
						throttlView.setText("Throttle value:      "
								+ new Integer(throttle).toString());

					}

					@Override
					public void onStartTrackingTouch(SeekBar seekBar) {

						System.out
								.println("start throttleValue is " + throttle);
						throttlView.setText("Throttle value:      "
								+ new Integer(throttle).toString());

					}

					@Override
					public void onStopTrackingTouch(SeekBar seekBar) {

						System.out.println("throttleValue is " + throttle);
						throttlView.setText("Throttle value:      "
								+ new Integer(throttle).toString());

					}

				});

		threadSeekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {

			@Override
			public void onProgressChanged(SeekBar seekBar, int progress,
					boolean fromUser) {

				num_threads = progress + 1;
				System.out.println("Number of Thread is " + num_threads);
				threadView.setText("Number of Thread:      "
						+ new Integer(num_threads).toString());
			}

			@Override
			public void onStartTrackingTouch(SeekBar seekBar) {

				System.out.println("Number of Thread is " + num_threads);
				threadView.setText("Number of Thread:      "
						+ new Integer(num_threads).toString());

			}

			@Override
			public void onStopTrackingTouch(SeekBar seekBar) {

				System.out.println("Number of Thread is " + num_threads);
				threadView.setText("Number of Thread:      "
						+ new Integer(num_threads).toString());

			}

		});

	}

	/***
	 * 
	 * used to display info in another class
	 *
	 */
	class displayHandler extends Handler {
		@Override
		public void handleMessage(Message msg) {

		}

		public void sleep(long delayMillis) {
			this.removeMessages(0);
			sendMessageDelayed(obtainMessage(0), delayMillis);
		}
	};

	public static void updateTransferAmount() {
		initiateTransfer.setText("Start tranfering "
				+ String.valueOf(TransferManager.transferAmount)
				+ " jobs to another node");

	}

	public static void updateJobsize() {
		jobsize.setText("Remain jobs:     "
				+ String.valueOf(Main.cut_pos.size()));

	}

	/**
	 * Wait for another side to connect and get into process phase
	 */
	protected void waitForConnection() {
		try {
			Toast.makeText(this, "in try of try-catch", Toast.LENGTH_LONG)
					.show();

			// creating the ServerSocket
			ServerSocket ss = new ServerSocket(Main.BOOTSRAPPORT);

			// Toast.makeText(this, "serverSocket created",
			// Toast.LENGTH_LONG).show();

			ClientS = ss.accept();

			// tv.setText("CONNECTION ESTABLISHED");
			Toast.makeText(this, "CONNECTION ESTABLISHED", Toast.LENGTH_LONG)
					.show();

			orgInput = readBytes();

			Toast.makeText(this, "SERVER BOOTSTRAP FINISHED", Toast.LENGTH_LONG)
					.show();

			// ipAddress = ClientS.getRemoteSocketAddress().toString();
			ipAddress = ClientS.getInetAddress().getHostAddress();

			Toast.makeText(this, ipAddress, Toast.LENGTH_LONG).show();

			// Toast.makeText(this, "get connected", Toast.LENGTH_LONG).show();
			threadSeekBar.setEnabled(false);
			ClientS.close();
			ss.close();

			System.out.println("FINISHED BOOTSTRAP");

			Main.func_kind = "PASSIVE";

			try {
				// new ProcessingPhase(orgInput, multiplier, 0, width,
				// "PASSIVE").process();
				new Thread(new ProcessingPhase(orgInput, multiplier, 0, width,
						"PASSIVE")).start();
			} catch (Exception e) {
				System.out.println("processingPhase" + e.toString());
				e.printStackTrace();
			}

		} catch (IOException e1) {
			Toast.makeText(this, "in catch of try-catch", Toast.LENGTH_LONG)
					.show();
			e1.printStackTrace();
			return;
		}
	}

	/**
	 * Read bytes from the socket. Data sent by another side
	 * @return  the bytes read
	 * @throws IOException
	 */
	public byte[] readBytes() throws IOException {
		// Again, probably better to store these objects references in the
		// support class
		Toast.makeText(this, "inside readBytes, but nothing read now",
				Toast.LENGTH_LONG).show();
		System.out.println("inside readBytes, but nothing read now");

		InputStream in = null;
		try {
			in = ClientS.getInputStream();
		} catch (IOException e) {
			e.printStackTrace();
			System.out.println("open input stream exception");
			throw new IOException();
		}
		DataInputStream dis = new DataInputStream(in);

		int len = 0;
		try {
			len = dis.readInt();
			multiplier = dis.readInt();
			width = dis.readInt();
		} catch (IOException e) {

			Toast.makeText(this, "read in", Toast.LENGTH_LONG).show();
			e.printStackTrace();
			System.out.println("reading integer exception");
			throw new IOException();

		}

		byte[] data = new byte[len];
		if (len > 0) {
			try {
				dis.readFully(data);
			} catch (IOException e) {
				Toast.makeText(this, "read fully", Toast.LENGTH_LONG).show();
				e.printStackTrace();
				System.out.println("reading fully byte data exception");
				throw new IOException();
			}
		}

		Toast.makeText(this, "Length: " + len, Toast.LENGTH_LONG).show();
		System.out.println("Length: " + len);

		Toast.makeText(this, "Multiplier: " + multiplier, Toast.LENGTH_LONG)
				.show();
		System.out.println("Multiplier: " + multiplier);

		Toast.makeText(this, "Width: " + width, Toast.LENGTH_LONG).show();
		System.out.println("Width: " + width);

		return data;
	}

	/**
	 * Connect to another side and get into the process phase
	 * @param ipAddress
	 */
	private void connect(String ipAddress) {
		Toast.makeText(this, ipAddress, Toast.LENGTH_LONG).show();
		Job job = null;
		try {
			Socket s = new Socket(ipAddress, BOOTSRAPPORT);
			System.out.println("Connected!!!");

			job = new Job(1111, 100, 10);

			multiplier = job.multiplier;
			width = job.width;

			orgInput = job.getWholeByte();
			sendBytes(job.getWholeByte(), 0, job.getWholeByte().length, s);

			s.close();

		} catch (UnknownHostException e) {
			Toast.makeText(this, "not created", Toast.LENGTH_LONG).show();
			System.out.println("CLIENT NOT CREATED");
			e.printStackTrace();
			return;
		} catch (IOException e) {
			System.out
					.println("IOE: Socket s = new Socket(ipAddress, BOOTSRAPPORT);");
			e.printStackTrace();
			return;
		}

		Toast.makeText(this, "CLIENT BOOTSTRAP FINISHED", Toast.LENGTH_LONG)
				.show();
		System.out.println("CLIENT BOOTSTRAP FINISHED");
		threadSeekBar.setEnabled(false);

		Main.func_kind = "ACTIVE";

		try {
			// new ProcessingPhase(orgInput, multiplier, 1, width,
			// "ACTIVE").process();
			new Thread(new ProcessingPhase(orgInput, multiplier, 1, width,
					"ACTIVE")).start();
		} catch (Exception e) {
			System.out
					.println("rocessingPhase(orgInput, multiplier, 1, width, \"ACTIVE\").process();"
							+ e.toString());
			e.printStackTrace();
		}

	}

	/**
	 * Send data to another side through socket
	 * @param myByteArray the data we need to send
	 * @param start the position send started
	 * @param len the data length of byte
	 * @param socket
	 * @throws IOException
	 */
	public void sendBytes(byte[] myByteArray, int start, int len, Socket socket)
			throws IOException {
		if (len < 0)
			throw new IllegalArgumentException("Negative length not allowed");
		if (start < 0 || start >= myByteArray.length)
			throw new IndexOutOfBoundsException("Out of bounds: " + start);
		// Other checks if needed.

		// May be better to save the streams in the support class;
		// just like the socket variable.
		OutputStream out = socket.getOutputStream();
		DataOutputStream dos = new DataOutputStream(out);

		Toast.makeText(this, "Before Write Length: " + len, Toast.LENGTH_LONG)
				.show();
		System.out.println("Before Write Length: " + len);

		Toast.makeText(this, "Before Write MULTIPLIER: " + multiplier,
				Toast.LENGTH_LONG).show();
		System.out.println("Before Write MULTIPLIER: " + multiplier);

		Toast.makeText(this, "Before Write width: " + width, Toast.LENGTH_LONG)
				.show();
		System.out.println("Before Write width: " + width);

		dos.writeInt(len);
		dos.writeInt(multiplier);
		dos.writeInt(width);
		if (len > 0) {
			dos.write(myByteArray, start, len);
		}
		Toast.makeText(this, "After writing: " + len, Toast.LENGTH_LONG).show();
		System.out.println("After writing: " + len);
	}

}
